package com.example.sources.servlet;

import com.example.sources.annotation.PathVariable;
import com.example.sources.annotation.RequestParam;
import com.example.sources.beans.factory.BeanFactory;
import com.example.sources.config.SystemConfig;
import com.example.sources.modelView.ModelAndView;
import com.example.sources.modelView.ViewUtil;
import com.example.sources.util.AnnotationUtil;
import com.example.sources.util.MapBean;
import javassist.*;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.List;
import java.util.Map;

/**
 * Created by zhu on 2016/5/9.
 */
public class DispatcherServlet extends GenericServlet implements Serializable{

    private HttpServletRequest httpServletRequest = null;
    private HttpServletResponse httpServletResponse = null;
    private ServletContext servletContext;

    @Override
    public void init() throws ServletException {
        List<MapBean> list = new AnnotationUtil().getRequestMapping();
        BeanFactory beanFactory = new BeanFactory();
        beanFactory.createFactory();
        Map<String,Object> map = beanFactory.getBeansMap();
        this.servletContext = getServletContext();
        servletContext.setAttribute("annotationList",list);
        servletContext.setAttribute("beanMap",map);
        super.init();
    }



    @Override
    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
        HttpServletRequest request;
        HttpServletResponse response;
        try {
            request = (HttpServletRequest)servletRequest;
            response = (HttpServletResponse)servletResponse;
            httpServletRequest = request;
            httpServletResponse = response;
        }catch (Exception e){
            throw new ServletException("non-HTTP request or response");
        }

        response.setContentType("text/html;charset=utf-8");
        response.setCharacterEncoding("utf-8");
        this.service(request,response);
    }

    public void service(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws ServletException, IOException {
        String url = servletRequest.getRequestURI();
        String queryString = servletRequest.getQueryString();
        String method = servletRequest.getMethod();
        MethodParamter methodParamter = new MethodParamter();
        List<MapBean> list = (List<MapBean>) getServletContext().getAttribute("annotationList");
        Map<String,Object> beanMap = (Map<String, Object>) getServletContext().getAttribute("beanMap");
        MapBean mapBean = null;

        for (MapBean m : list) {
            if ((m.getPath().equals(url) || PathVariableParam.isPathVarible(url, m, methodParamter))&&m.getRequestmethod().equals(method)) {
                if (queryString != null){
                    RequestParameter.setRequestParamValue(queryString,methodParamter);
                }
                mapBean = m;
                break;
            }
        }
        if (mapBean!=null) {
            try {
                Class<?> cla = Class.forName(mapBean.getClassname());
                Method[] methods = cla.getMethods();

                Method thismethod = null;
                for (Method method1 : methods) {
                    if (method1.getName().equals(mapBean.getMethodname())) {
                        thismethod = method1;
                    }
                }
                Class<?>[] classes = getParamterClass(thismethod, mapBean);
                Object o = beanMap.get(mapBean.getClassname().split("\\.")[mapBean.getClassname().split("\\.").length - 1]);
                Object[] objects = setParamterValue(mapBean,thismethod,methodParamter);
                if (thismethod.getReturnType().getName().contains("ModelAndView")){
                    ModelAndView m = (ModelAndView) cla.getMethod(mapBean.getMethodname(), classes).invoke(o, objects);
                    servletResponse.getWriter().write(ViewUtil.setRespView(servletRequest, m));
                }else {


                String result = (String) cla.getMethod(mapBean.getMethodname(), classes).invoke(o,objects);
                if (mapBean.getRespType().equals(SystemConfig.ResponsePage)) {
                    servletRequest.getRequestDispatcher("/" + result).forward(servletRequest, servletResponse);
                } else {
                    servletResponse.getWriter().write(result);
                }

                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }else {

            servletResponse.getWriter().write("error");
        }
    }


    public Object[] setParamterValue(MapBean mapBean,Method method,MethodParamter methodParamter) {

        Object[] objects = new Object[method.getParameters().length];
        ClassPool pool = ClassPool.getDefault();
        pool.insertClassPath(new ClassClassPath(this.getClass()));
        String[] paramterName = null;
        CtClass cc;
        try {
            cc = pool.get(mapBean.getClassname());

            CtMethod cm = cc.getDeclaredMethod(mapBean.getMethodname());

            MethodInfo info = cm.getMethodInfo();

            CodeAttribute codeAttribute = info.getCodeAttribute();

            LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute.getAttribute(LocalVariableAttribute.tag);

            int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;

            String[] names=new String[cm.getParameterTypes().length];
            paramterName = new String[names.length];
            for (int i = 0; i < names.length; i++)
            {

                names[i] = attr.variableName(i + pos);
                paramterName[i] = names[i];
            }

        } catch (NotFoundException e) {
            e.printStackTrace();
        }


        int flag = 0;

        for (Parameter parameter:method.getParameters()){
            Object o = null;

            if (parameter.getAnnotation(PathVariable.class)!=null){
                o = methodParamter.getParamterValue(paramterName[flag]);
            }else if (parameter.getAnnotation(RequestParam.class)!=null){
                o = methodParamter.getParamterValue(paramterName[flag]);
            }else {

                if (parameter.getType().getName().contains("HttpServletRequest")){
                    o = httpServletRequest;
                }else if (parameter.getType().getName().contains("HttpServletResponse")){
                    o = httpServletResponse;
                }
            }
            objects[flag] = o;
            flag++;

        }

        return objects;
    }

    public Class<?>[] getParamterClass(Method method,MapBean mapBean){
        Class<?>[] classes = null;


                Class<?>[] parameterTypes = method.getParameterTypes();
                for (int i = 0; i < parameterTypes.length; i++) {
                    classes = parameterTypes;


            }
        return classes;
        }

    }

